/**
 * 一个简单js动态加载器，用来加载数量比较大的js文件，适用于用ExtJs来做开发。
 * ·define方法：用于定义界面模块，每个要被动态加载的js模块的格式如下：
 *   define(function(){
 *     //....
 *     return result;
 *   })
 * ·use方法：用于加载界面模块，使用方法如下：
 *   use('js模块名', function(result){
 *     //使用result的代码，这里的result即为define定义中返回的result
 *   })
 *   其中，js模块名与js文件的目录结构保持一致，但模块名使用(.)来做分隔符，而不是(/)。
 *   所以，保存js模块的文件和目录名称中不能出现(.)符号。另外，模块名也不包括文件后缀名(.js)。
 * ·use2方法，功能与use方法类似，区别在于use方法必须使用回调方法来处理结果，
 *   而use2方法是同步的，不需要使用回调方法，例如：
 *   var result = use2('js模块名');  //这里的result即为define定义中返回的result
 *   （
 *   	use方法与use2的区别：
 *   	·use方法的优点：调试方便，js模块出错后浏览器的报错能精确到某一行；
 *      ·use方法的缺点：必须使用回调方法来处理结果，造成编写代码有所困难。
 *      ·use2方法的优点：跟调用本地方法一样直接处理结果，编写代码简单。
 *      ·use2方法的缺点：调试不方便，js模块出错后浏览器无法确定是在哪里出错。
 *        一般情况下，建议使用use方法，迫不得已才使用use2方法。
 *   ）
 * @author Linzongxue
 * @create_date 2011-05-14
 */
var loader = {
	//private: 加载器的配置，用config方法来设定
	cfg: {
		base: '',
		debug: false
	},
	//private:保存js模块执行的结果
	cache: [],
	//private: use方法当前要加载的js路径，用于保证同步和缓存
	curLoadingJs: null,
	//private: use2方法当前要加载的js路径
	curLoadingJs2: null,
	/**
	 *  加载器的配置设定方法，可设置的参数为base和debug
	 */
	config: function(cfg){
		for (var p in cfg){
			this.cfg[p] = cfg[p];
		}
		//如果设置了base，确保base由"/"结尾
		var base = this.cfg.base;
		if (base && base.lastIndexOf('/') !== base.length - 1){
			this.cfg.base += '/';
		}
	},
	/**
	 * 来用定义js模块的方法，该方法的执行结果被存放到cache中，
	 * 如果执行结果不返回任何值，相应的cache为一个空对象。
	 */
	define: function(fn){
		var js = this.curLoadingJs2 || this.curLoadingJs;
		if (!this.isFn(fn)){
			alert('以下文件未按格式正式定义：\n' + js + '\ndefine方法内必须是一个方法');
			return;
		}
		this.cache[js] = fn() || {};
	},
	/**
	 * 加载js模块的方法，虽为异步加载，但被限制为每次只能加载一个js文件。
	 * 并且必须等待上一个文件加载完成，下一个文件才能开始加载。
	 * @param moduleName js模块的名称，与存放目录的结构一致，分隔附为(.)，不包括后缀名(.js)
	 * @param userCallback 加载模块后的回调函数
	 * @scope 回调函数中this的指向，只为方便写代码，为可选参数
	 */
	use: function(moduleName, userCallback, scope){
		if (typeof(moduleName) !== 'string'){
			alert('use方法的第一个参数必须是字符串，当前是' + typeof(moduleName));
			return;
		}
		var uri = this.nameToUri(moduleName);
		if (this.cache[uri] && !this.cfg.debug){//已加载过，也不是debug模式，不重复加载
			if (this.isFn(userCallback)){
				userCallback.call(scope, this.cache[uri]);
			}
			return;
		}
		if (this.cfg.debug && !this.isIE){ //debug模式，不缓存js(不支持IE)
			uri += '?_t=' + new Date().getTime();
		}
		this.loadJs(uri, userCallback, scope);
	},
	/**
	 * 同步加载js文件
	 */
	use2: function(moduleName, params){
		var uri = this.nameToUri(moduleName);
		if (this.cache[uri] && !this.cfg.debug){
			return this.cache[uri];
		}
		this.curLoadingJs2 = uri;
		if (this.cfg.debug && !this.isIE){ //debug模式，不缓存js(不支持IE)
			uri += '?_t=' + new Date().getTime();
		}
		var http = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
		http.open('GET', uri, false);
		http.send();
		try{
			new Function(http.responseText)(); //执行被加载的js文件，define方法被执行
			var result = this.cache[this.curLoadingJs2];
			if (this.cfg.debug){ //debug模式不缓存执行结果
				delete this.cache[this.curLoadingJs2];
			}
			this.curLoadingJs2 = null;
			return result;
		}
		catch(e){
			alert('加载以下js文件出错：\n' + this.curLoadingJs2 + '\n出错信息：\n' + e);
		}
		return null;
	},
	//private
	loadJs: function(jsSrc, callback, scope){
		var me = this;
		if (this.curLoadingJs){ //上次加载未完成，等待一小段时间再继续
			setTimeout(function(){me.loadJs(jsSrc, callback, scope);}, 30);
			return;
		}
		this.curLoadingJs = jsSrc;//记住当前正在加载的js
		var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;
		//js文件加载完成后执行的方法
		var cb = function(error){
			me.afterJsLoad(callback, scope);
			head.removeChild(node);
			//me.debug(me.curLoadingJs);
			me.curLoadingJs = null;
		}
		//创建"script"节点来加载js文件
		var node = document.createElement("script");
		node.type = "text/javascript";
		node.src = jsSrc;
		if (node.addEventListener){
			node.addEventListener('load', cb, false);
			node.addEventListener('error', cb, false);
		}
		else if (node.attachEvent){
			node.attachEvent('onreadystatechange', function() {
		        if (node.readyState === 'loaded' || node.readyState === 'complete') {
		        	cb.call();
		        }
		    });
		}
		else{
			alert('浏览器不支持动态加载js');
			return;
		}
		head.insertBefore(node, head.firstChild);
	},

	//private
	afterJsLoad: function(userCallback, scope){
		//加载js文件后，执行结果被保存在cache中，
		//这是因为js模块文件调用了define方法。
		var result = this.cache[this.curLoadingJs];
		if (!result){
			alert('加载以下js文件出错:\n' + this.curLoadingJs.replace(/\?.*/, ''));
			return;
		}
		if (this.cfg.debug){ //debug模式不缓存
			delete this.cache[this.curLoadingJs];
		}
		if (this.isFn(userCallback)){
			userCallback.call(scope, result);
		}
	},

	//private
	nameToUri: function(jsModuleName){
		var cfg = this.cfg;
		var url = jsModuleName.replace(/\./g, '/');
		if (cfg.base){
			url = cfg.base + url;
		}
		return url + ".js";
	},
	//private
	debug: function(msg){
		if(typeof console !== 'undefined'){
			console['debug'](msg);
		}
	},
	//private
	isIE: window.ActiveXObject ? true : false,
	//private
	isFn: function(fn){
		return typeof fn === "function";
	},
	isObj: function(obj){
		return typeof obj === 'object';
	}
};

/**
 * 绑定常用的方法为全局方法
 */
this.define = function(fn){
	loader.define(fn);
};
this.use = function(jsModule, cb, scope){
	loader.use(jsModule, cb, scope);
};
this.use2 = function(jsModule){
	return loader.use2(jsModule);
}

/*
(function(){
	var mainJs = document.getElementsByTagName('script')[0].getAttribute('main');
	mainJs && loadLib(mainJs);
})();
*/